Skip to content

[None][fix] Fix one-model MTP KV cache accounting - #17264

Open
2ez4bz wants to merge 3 commits into
NVIDIA:mainfrom
2ez4bz:dev-flashinfer-mtp-ima
Open

[None][fix] Fix one-model MTP KV cache accounting#17264
2ez4bz wants to merge 3 commits into
NVIDIA:mainfrom
2ez4bz:dev-flashinfer-mtp-ima

Conversation

@2ez4bz

@2ez4bz 2ez4bz commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Dev Engineer Review

  • One-model MTP scheduling synchronizes Python-side and C++ draft-token state.
  • Drafting stops when requests can exceed the legal target-position limit.
  • Speculative KV accounting uses the prompt KV boundary for the first generation step when dynamic KV-length correction is unavailable.
  • FlashInfer separates logical KV lengths from reserved generation-page capacity.
  • KV-length offsets are applied and restored across speculative decoding and CUDA graph execution.
  • These changes prevent overstated cached lengths, invalid FlashInfer page access, and incorrect KV scheduler capacity planning.
  • Added reserved-block-count APIs to KVCacheManager and KVCacheManagerV2.
  • Added context-manager support to BaseResourceManager.
  • No configuration or test-list files changed.

QA Engineer Review

  • Added tests for one-model MTP draft-token normalization, Python draft-token preservation, and sequence-position limits.
  • Added tests for prompt-boundary KV accounting and FlashInfer KV-offset application and restoration.
  • The modified test functions are not referenced in tests/integration/test_lists/ based on the available repository coverage entries.
  • Verdict: needs follow-up.

Description

  • Why?

The first overlapped MTP generation step could overstate its cached length and access an invalid FlashInfer page, causing a CUDA illegal memory access. The KV scheduler also lacked the Python-side draft-token count needed for correct capacity planning.

  • What?

Keep the Python and C++ draft-token state synchronized, and use the prompt KV boundary when generation immediately follows context for backends without dynamic KV-length correction.

Test Coverage

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@2ez4bz

2ez4bz commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The change updates speculative decoding across executor scheduling and FlashInfer KV-cache handling. It preserves Python draft tokens, applies and restores backend KV-length offsets, separates logical lengths from reserved page-table capacity, and adds regression coverage.

Changes

Speculative decoding and KV-cache updates

Layer / File(s) Summary
Logical KV lengths and generation page tables
tensorrt_llm/_torch/attention_backend/flashinfer.py, tensorrt_llm/_torch/metadata.py, tensorrt_llm/_torch/pyexecutor/resource_manager.py, tensorrt_llm/_torch/pyexecutor/kv_cache_manager_v2.py, tests/unittest/_torch/attention/test_flashinfer_attention.py
FlashInfer tracks logical KV lengths separately from reserved generation page-table capacity. Speculative offsets update positions and decode-wrapper buffers. Cache managers expose reserved block counts.
Executor integration and cache boundaries
tensorrt_llm/_torch/pyexecutor/model_engine.py, tests/unittest/_torch/executor/test_pytorch_model_engine.py
The executor applies and restores backend offsets, enables full generation page tables when supported, and uses the prompt boundary for the first overlapping generation iteration.
One-model MTP draft state and sequence-limit safety
tensorrt_llm/_torch/pyexecutor/py_executor.py, tests/unittest/_torch/executor/test_py_executor.py
One-model MTP disables drafting when target positions reach legal limits and preserves populated Python draft tokens. Tests cover draft normalization and sequence limits.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler
  participant ModelEngine
  participant FlashInfer
  participant DecodeWrapper
  Scheduler->>ModelEngine: build speculative generation metadata
  ModelEngine->>FlashInfer: apply KV-length offsets
  FlashInfer->>DecodeWrapper: publish logical KV lengths
  ModelEngine->>FlashInfer: prepare and plan decode
  ModelEngine->>FlashInfer: restore KV-length offsets
Loading

Suggested labels: api-compatible

Suggested reviewers: schetlur-nv, chienchunhung, liji-nv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the fix for one-model MTP KV cache accounting.
Description check ✅ Passed The description explains the problem and solution and includes the checklist, but the Test Coverage section is empty despite added regression tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch dev-flashinfer-mtp-ima
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/_torch/executor/test_py_executor.py`:
- Around line 2010-2013: Update the draft-token assertions in the affected
executor test to verify values, not only lengths: compare both
gen.py_draft_tokens and disagg_gen.py_draft_tokens against [0] *
self.MAX_TOTAL_DRAFT_TOKENS. Keep the existing num_draft_tokens assertions
unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: cfdc40e2-408d-41fd-8732-7561ca155193

📥 Commits

Reviewing files that changed from the base of the PR and between be93500 and 0baecbe.

📒 Files selected for processing (4)
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tests/unittest/_torch/executor/test_py_executor.py
  • tests/unittest/_torch/executor/test_pytorch_model_engine.py

Comment thread tests/unittest/_torch/executor/test_py_executor.py Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63826 [ run ] triggered by Bot. Commit: 0baecbe Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63826 [ run ] completed with state SUCCESS. Commit: 0baecbe
/LLM/main/L0_MergeRequest_PR pipeline #51767 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@BowenFu

BowenFu commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Could you clarify how these two hunks relate?

  • On the one-model path, py_draft_tokens now contains dummy values when _prepare_tp_inputs first reads it. Please explain why setting only the C++ draft_tokens count would not satisfy scheduler accounting.
  • Is the py_decoding_iter == 0 cached_token_num correction required because that list is now non-empty? If the changes are coupled, please document the invariant and add a focused test. If they fix independent bugs, separate commits would make them easier to validate and revert.

The kv_lens_cuda backend guard looks appropriately scoped.

@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch from 0baecbe to 990299f Compare August 5, 2026 16:53
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/_torch/executor/test_py_executor.py`:
- Around line 2036-2037: Update the sampler draft setup in the relevant executor
test to use a nonempty list shorter than self.MAX_TOTAL_DRAFT_TOKENS, while
retaining the assertion that request.draft_tokens uses the full scheduler budget
and request.py_draft_tokens preserves the supplied shorter list.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7989f080-973b-451a-9155-656f54711e18

📥 Commits

Reviewing files that changed from the base of the PR and between 1dfb7b1 and 990299f.

📒 Files selected for processing (4)
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tests/unittest/_torch/executor/test_py_executor.py
  • tests/unittest/_torch/executor/test_pytorch_model_engine.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/unittest/_torch/executor/test_pytorch_model_engine.py
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tensorrt_llm/_torch/pyexecutor/model_engine.py

Comment thread tests/unittest/_torch/executor/test_py_executor.py Outdated
@2ez4bz

2ez4bz commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

@BowenFu

These changes address two parts of the same MTP + FlashInfer + block-reuse failure path.

The python and C++ draft-token representations must both be populated because they have different consumers: the C++ micro-batch scheduler uses draft_tokens, while python scheduling and model-input preparation use py_draft_tokens.

The first-generation cached_token_num correction handles the same context-to-generation transition: a previous overlap tensor exists, but no speculative target forward has populated draft-token KV entries yet. Without using the prompt boundary, FlashInfer can access an invalid reused page. The focused tests cover the two state assumptions independently.

I’m keeping them together because both are needed for this MTP + FlashInfer + block-reuse fix.

@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch from 990299f to 9ff8de8 Compare August 5, 2026 17:26
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@2ez4bz

2ez4bz commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64088 [ run ] triggered by Bot. Commit: 9ff8de8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64088 [ run ] completed with state SUCCESS. Commit: 9ff8de8
/LLM/main/L0_MergeRequest_PR pipeline #52015 completed with status: 'FAILURE'

CI Report

⚠️ Multi-GPU Label Required:
Multi-GPU tests require the ci: full pre-merge approved label on this PR. Ask a member of NVIDIA/trt-llm-ci-approvers to add the label, then re-trigger CI with the same bot command (no rebase needed).

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@2ez4bz

2ez4bz commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64146 [ run ] triggered by Bot. Commit: 9ff8de8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64146 [ run ] completed with state FAILURE. Commit: 9ff8de8
/LLM/main/L0_MergeRequest_PR pipeline #52064 completed with status: 'FAILURE'

CI Report

⚠️ Multi-GPU Label Required:
Multi-GPU tests require the ci: full pre-merge approved label on this PR. Ask a member of NVIDIA/trt-llm-ci-approvers to add the label, then re-trigger CI with the same bot command (no rebase needed).

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@2ez4bz

2ez4bz commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64208 [ run ] triggered by Bot. Commit: 9ff8de8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64208 [ run ] completed with state FAILURE. Commit: 9ff8de8
/LLM/main/L0_MergeRequest_PR pipeline #52120 completed with status: 'FAILURE'

CI Report

⚠️ Multi-GPU Label Required:
Multi-GPU tests require the ci: full pre-merge approved label on this PR. Ask a member of NVIDIA/trt-llm-ci-approvers to add the label, then re-trigger CI with the same bot command (no rebase needed).

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tensorrt_llm/_torch/pyexecutor/model_engine.py (1)

2968-2992: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Apply FlashInfer offsets to extend-context rows.

FlashInferAttentionMetadata.apply_spec_decode_kv_lens_offsets() returns when num_generations == 0. In extend-context mode, extend requests are included in num_contexts, so their rows are [num_contexts - num_chunked_ctx_requests:num_contexts] and are not corrected. Add equivalent chunked-context handling in both preprocessing and restoration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/pyexecutor/model_engine.py` around lines 2968 - 2992,
Update both preprocessing and restoration flows around
apply_spec_decode_kv_lens_offsets so FlashInfer metadata also adjusts chunked
extend-context rows. When num_chunked_ctx_requests > 0, apply
previous_kv_lens_offsets_cuda to rows [num_ctx_requests -
num_chunked_ctx_requests:num_ctx_requests], including cases with zero
generations; preserve the existing generation-row handling for other requests.
🧹 Nitpick comments (2)
tensorrt_llm/_torch/pyexecutor/model_engine.py (2)

3485-3492: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated use_full_generation_page_table gating condition.

This exact four-clause expression is repeated verbatim at Line 5098-5101 in _prepare_tp_inputs. Both sites gate the same correctness-critical feature (whether generation rows get the full reservation-width page table). If one site's condition changes in the future (e.g. a new gating factor) without the other, the two code paths silently diverge on this KV-cache-accounting decision. Extract a small helper, e.g. self._should_use_full_generation_page_table(spec_config, attn_metadata).

♻️ Proposed refactor
+    def _should_use_full_generation_page_table(
+            self, spec_config: Optional[DecodingBaseConfig],
+            attn_metadata: AttentionMetadata) -> bool:
+        return (self.enable_spec_decode and not self._disable_overlap_scheduler
+                and getattr(spec_config, '_use_shared_kv_cache', False)
+                and hasattr(attn_metadata, 'apply_spec_decode_kv_lens_offsets'))
+
     def _prepare_incremental_update_metadata(
             self,
             ...
         attn_metadata.kv_cache_params = KVCacheParams(
             use_cache=True,
             num_cached_tokens_per_seq=num_cached_tokens_per_seq,
             num_extra_kv_tokens=get_num_extra_kv_tokens(spec_config),
-            use_full_generation_page_table=(
-                self.enable_spec_decode and not self._disable_overlap_scheduler
-                and getattr(spec_config, '_use_shared_kv_cache', False) and
-                hasattr(attn_metadata, 'apply_spec_decode_kv_lens_offsets')))
+            use_full_generation_page_table=self._should_use_full_generation_page_table(
+                spec_config, attn_metadata))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/pyexecutor/model_engine.py` around lines 3485 - 3492,
Extract the repeated four-clause `use_full_generation_page_table` condition from
the KVCacheParams construction and `_prepare_tp_inputs` into a shared helper
such as `_should_use_full_generation_page_table(spec_config, attn_metadata)`.
Replace both inline expressions with calls to this helper while preserving the
existing gating behavior.

5094-5101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated use_full_generation_page_table gating condition (second site).

Same expression as Line 3489-3492 in _prepare_incremental_update_metadata. See the comment on that range; both should call one shared helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/pyexecutor/model_engine.py` around lines 5094 - 5101,
Extract the repeated use_full_generation_page_table eligibility expression into
a shared helper, then update both _prepare_incremental_update_metadata and this
KVCacheParams construction to call it. Preserve the existing checks for spec
decoding, overlap scheduling, shared KV cache, and
apply_spec_decode_kv_lens_offsets.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 3313-3337: Pass the resolved maximum sequence length into the
AutoDeploy PyExecutor constructor so self.max_seq_len is initialized, using the
engine cache sequence interface’s max_seq_len value. Ensure
_one_model_mtp_batch_needs_zero_draft can compare max_target_position against a
valid limit during one-model MTP drafting.

---

Outside diff comments:
In `@tensorrt_llm/_torch/pyexecutor/model_engine.py`:
- Around line 2968-2992: Update both preprocessing and restoration flows around
apply_spec_decode_kv_lens_offsets so FlashInfer metadata also adjusts chunked
extend-context rows. When num_chunked_ctx_requests > 0, apply
previous_kv_lens_offsets_cuda to rows [num_ctx_requests -
num_chunked_ctx_requests:num_ctx_requests], including cases with zero
generations; preserve the existing generation-row handling for other requests.

---

Nitpick comments:
In `@tensorrt_llm/_torch/pyexecutor/model_engine.py`:
- Around line 3485-3492: Extract the repeated four-clause
`use_full_generation_page_table` condition from the KVCacheParams construction
and `_prepare_tp_inputs` into a shared helper such as
`_should_use_full_generation_page_table(spec_config, attn_metadata)`. Replace
both inline expressions with calls to this helper while preserving the existing
gating behavior.
- Around line 5094-5101: Extract the repeated use_full_generation_page_table
eligibility expression into a shared helper, then update both
_prepare_incremental_update_metadata and this KVCacheParams construction to call
it. Preserve the existing checks for spec decoding, overlap scheduling, shared
KV cache, and apply_spec_decode_kv_lens_offsets.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c80bbdf5-195b-4a3d-93f2-146f12daecfb

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba3de1 and 275e929.

📒 Files selected for processing (8)
  • tensorrt_llm/_torch/attention_backend/flashinfer.py
  • tensorrt_llm/_torch/metadata.py
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py
  • tests/unittest/_torch/attention/test_flashinfer_attention.py
  • tests/unittest/_torch/executor/test_py_executor.py
  • tests/unittest/_torch/executor/test_pytorch_model_engine.py

Comment thread tensorrt_llm/_torch/pyexecutor/py_executor.py
Comment thread tensorrt_llm/_torch/attention_backend/flashinfer.py
Comment thread tensorrt_llm/_torch/attention_backend/flashinfer.py
Comment thread tensorrt_llm/_torch/attention_backend/flashinfer.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/py_executor.py Outdated
@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch from 275e929 to 55330f2 Compare August 10, 2026 06:22
Comment thread tensorrt_llm/_torch/pyexecutor/model_engine.py Outdated
Comment thread tests/unittest/_torch/executor/test_py_executor.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tensorrt_llm/_torch/attention_backend/flashinfer.py (2)

23-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use built-in generic annotations.

Replace the new List[int] annotations with list[int]. Remove List from the import only if no remaining annotation needs it.

As per coding guidelines, “Use Python 3.10+” and “prefer built-in generic types.”

Also applies to: 99-103

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/attention_backend/flashinfer.py` around lines 23 - 24,
Update the annotations in flashinfer.py, including the additional locations
around lines 99–103, to use the built-in list[int] syntax instead of List[int].
Remove List from the typing import only if no other annotation in the file still
references it.

Source: Coding guidelines


801-808: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the offset contract explicitly.

Add Google-style Args: documentation. State the ordering and sign of offsets, the tokens_per_generation token mapping, and the invariant that restore=True exactly reverses the mutation. PyTorchModelEngine depends on this contract for both generation and chunked-context rows.

As per coding guidelines, use Google-style docstrings for functions. Based on PR objectives, this offset invariant spans Python scheduling and FlashInfer runtime state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/attention_backend/flashinfer.py` around lines 801 - 808,
The docstring for the FlashInfer runtime-state rewind function must explicitly
document its offset contract. Add a Google-style Args section describing offset
ordering and sign, how tokens_per_generation maps tokens, and that restore=True
exactly reverses the mutation; keep the contract applicable to both generation
and chunked-context rows used by PyTorchModelEngine.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_torch/attention_backend/flashinfer.py`:
- Around line 1519-1537: Update _do_plan_mla_decode() so kv_len_arr uses logical
KV lengths from _logical_kv_lens when _uses_full_generation_page_table is
enabled, rather than reservation-width paged_kv_indptr_decode values. Apply
overlap offsets to the logical lengths before MLA planning, while preserving
existing behavior for non-reserved page tables. Add focused MLA tests covering
both a reserved page and a nonzero overlap offset.

---

Nitpick comments:
In `@tensorrt_llm/_torch/attention_backend/flashinfer.py`:
- Around line 23-24: Update the annotations in flashinfer.py, including the
additional locations around lines 99–103, to use the built-in list[int] syntax
instead of List[int]. Remove List from the typing import only if no other
annotation in the file still references it.
- Around line 801-808: The docstring for the FlashInfer runtime-state rewind
function must explicitly document its offset contract. Add a Google-style Args
section describing offset ordering and sign, how tokens_per_generation maps
tokens, and that restore=True exactly reverses the mutation; keep the contract
applicable to both generation and chunked-context rows used by
PyTorchModelEngine.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 67cd2d73-3769-4cf3-802d-c37ce54b73d9

📥 Commits

Reviewing files that changed from the base of the PR and between 275e929 and 55330f2.

📒 Files selected for processing (8)
  • tensorrt_llm/_torch/attention_backend/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/kv_cache_manager_v2.py
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py
  • tests/unittest/_torch/attention/test_flashinfer_attention.py
  • tests/unittest/_torch/executor/test_py_executor.py
  • tests/unittest/_torch/executor/test_pytorch_model_engine.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tests/unittest/_torch/executor/test_pytorch_model_engine.py
  • tests/unittest/_torch/executor/test_py_executor.py
  • tests/unittest/_torch/attention/test_flashinfer_attention.py

Comment thread tensorrt_llm/_torch/attention_backend/flashinfer.py
@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch 2 times, most recently from d308f32 to c318de4 Compare August 10, 2026 23:09
Comment thread tests/unittest/_torch/sampler/test_torch_sampler.py
@2ez4bz

2ez4bz commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65147 [ run ] triggered by Bot. Commit: c318de4 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65147 [ run ] completed with state FAILURE. Commit: c318de4
/LLM/main/L0_MergeRequest_PR pipeline #52942 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@2ez4bz

2ez4bz commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65201 [ run ] triggered by Bot. Commit: c318de4 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65201 [ run ] completed with state FAILURE. Commit: c318de4
/LLM/main/L0_MergeRequest_PR pipeline #52988 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@chienchunhung chienchunhung left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I left one non-blocking comment about AutoDeploy path.

Comment thread tensorrt_llm/_torch/pyexecutor/py_executor.py Outdated
@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch from c318de4 to 3777d74 Compare August 12, 2026 04:22
BowenFu
BowenFu approved these changes Aug 12, 2026
@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch from 3777d74 to abbd34f Compare August 12, 2026 04:48
@2ez4bz
2ez4bz requested a review from a team as a code owner August 12, 2026 04:48
BowenFu
BowenFu approved these changes Aug 12, 2026
if spec_config is not None and spec_config.is_linear_tree else
self.model_engine.max_total_draft_tokens)

if self._one_model_mtp_batch_needs_zero_draft(scheduled_batch):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this boundary decision group-uniform under attention DP? Each rank schedules different requests, so one rank can set runtime_draft_len=0 and return through skip_drafting() while a peer keeps K and enters DeepseekV3MTPHead's LM-head-TP all-gather. That leaves an unmatched collective and hangs the group. Please OR the unsafe flag across the TP/ADP group before changing the batch-wide draft length, and add a two-rank near-limit test (optional).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

_handle_dynamic_draft_len() now computes a local runtime_draft_len first, ORs the unsafe flag across the TP/ADP group, and only then mutates runtime_draft_len and request state. All three call sites are already inside if can_queue, which is itself a TP allgather under ADP, so every rank enters the new collective exactly once in the same position; the non-ADP path stays collective-free.

Note that as a result, the diff is quite large: the speculation_permanently_disabled is an early return, and the pad / truncate loop moved to the tail behind if not dynamic_draft_len_enabled: return, and _one_model_mtp_batch_needs_zero_draft() now takes runtime_draft_len as a parameter since it can no longer read the not-yet-committed engine field. Note one deliberate semantic flip inside it: runtime_draft_len == 0 now returns True instead of False, so a locally selected zero also participates in the OR.

One more thing worth pointing at in the diff, since it is easy to read as unrelated: the clear is now if needs_zero_draft or runtime_draft_len == 0.

My first pass at this restructure folded the old unconditional speculation_permanently_disabled clear into the needs_zero_draft block, which is gated on is_mtp_eagle_one_model(). That silently dropped the clear for vanilla MTP and Eagle3 one-model, leaving K python draft tokens alongside runtime_draft_len == 0. Clearing on any committed zero restores it; test_permanent_disable_clears_non_mtp_eagle_draft_tokens is the corresponding test.

# disable drafting for every generation request in this batch.
for request in scheduled_batch.generation_requests:
request.py_draft_tokens = []
self.model_engine.runtime_draft_len = 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This zero-draft decision is lost for dynamic-tree MTP: overlap still supplies next_draft_tokens_device, so _prepare_tp_inputs() classifies the request as extend and then unconditionally resets runtime_draft_len to max_total_draft_tokens for non-linear trees. A near-limit max_draft_len=3, topK=4 request therefore executes all 12 tree tokens after this code selected zero. Can we preserve the explicit zero through TP-input preparation and extend the regression through that stage?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

_prepare_tp_inputs() no longer rewrites runtime_draft_len unconditionally for non-linear trees; the normalization is now skipped when the executor already selected zero.

This also explains the comment-only change at can_promote_spec_decode: its old justification no longer holds.

Regression extended past _handle_dynamic_draft_len() and through _prepare_tp_inputs() with a non-None next_draft_tokens_device, and is now tested in test_dynamic_tree_prepare_preserves_explicit_zero_draft.

# reads back in `_prepare_tp_inputs` - and would also suppress the
# `current_num_draft_tokens == 0` signal that `_handle_dynamic_draft_len` uses to
# request a one-hot draft-probs placeholder under rejection sampling.
if not request.py_draft_tokens:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we preserve the empty Python-side draft signal until _handle_dynamic_draft_len() consumes it? After a K=0 iteration, SpecSampler writes py_draft_tokens=[]; filling it here before the handler makes py_needs_onehot_draft_probs stay false. Since that slot produced no proposal row at K=0, the next rejection step gathers stale draft_probs and biases sampling. The scheduler only needs request.draft_tokens, so the Python placeholder can be deferred; please add a rejection-enabled K->0->K regression.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Note that deferring the python placeholder would regress this PR's capacity fix, because get_draft_token_length() reads py_draft_tokens and SchedulerV2, KV allocation, and resource management all budget from it.

Instead, the zero-proposal fact is captured before the placeholder is inserte, and _prepare_tp_inputs() consumes it exactly once. Timing works out because a K=0 request has empty py_draft_tokens and therefore lands in generation_requests, not extend_requests. The flag isn't consumed until the resumed K>0 step, which is when the one-hot row is actually needed.

Covered by test_one_model_mtp_preserves_zero_proposal_signal_for_rejection.

self._logical_kv_lens[row_slice].add_(runtime_offsets,
alpha=direction)

# Keep the host-preallocated page structure intact. In particular, `last_page_len` must stay

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page reservation can stay intact, but paged prefill still derives its causal KV length from this unchanged indptr/last-page pair (and cached-MLA context does likewise). With cached=31, q_len=4, and offset=-3, appends move to 28..31 while prefill still aligns Q to a 35-token KV, admitting future/stale slots. Can we publish the corrected logical context lengths to the prefill/MLA-context runtime, or reject this extend-context configuration? The added metadata-only SA test never reaches the real extend-context plan.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and I took the narrow option: overlap is now disabled for FlashInfer + any speculation mode that uses extend_ctx, rather than publishing corrected logical context lengths.

extend_ctx() returns not issubclass(backend, TrtllmAttention) unless use_one_engine(), so for FlashInfer the gate condition not use_one_engine() is exactly "this run would take the extend-context path" -> one-engine FlashInfer speculation keeps overlap.

A real device-side logical-length contract for paged prefill and cached MLA context (page-boundary rewinds, CUDA-graph-stable buffers, validated against an actual plan/run rather than metadata mutation) is materially larger, so I think it should be punted.

One qualification on severity, since it changes how you may want to weigh this: I could not find a public configuration that reaches the FlashInfer extend-context path today:

extend_ctx requires not use_one_engine(), and combined with support_overlap_scheduler(), that leaves exactly has_draft_model() - EAGLE3 two-model, MTP-Eagle two-model, and DraftTarget two-model. All three are currently unreachable. So I believe the stale-length bug you identified is real but currently latent, and this gate is a guard for retained legacy / internal states rather than a fix for a live path. The test exercises it by mutating that private flag and is labeled as such.

For diff-reading: this is why the inline overlap check in create_py_executor was extracted into _disable_unsupported_speculative_overlap_scheduler().

# one iteration, so the sampler's first increment has not landed yet at this point
# and only here. This branch already assumes that same batch-to-batch continuity
# (previous_batch_idx indexes the immediately preceding batch's device tensors).
if (request.py_decoding_iter == 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes only the first overlap generation for metadata without dynamic KV lengths. On every later step the host cached count is still the speculative upper bound, while the accepted-length correction is device-side; VanillaAttentionMetadata has neither kv_lens_cuda nor apply_spec_decode_kv_lens_offsets(). For P=5, width=4, and one accepted token, Vanilla uses cached=9 although the committed boundary is 6, even as positions rewind to 6. Can we implement the dynamic-length contract for these backends or reject overlap speculation for them?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good catch!

Overlap speculation now disabled for the vanilla backend, and I removed the py_decoding_iter == 0 special case outright so nothing implies later steps are safe.

This is covered by the new TestUnsupportedSpeculativeOverlapScheduler, which asserts vanilla + speculation disables overlap, and that an already-disabled config is left alone.

This is the entrypoint API to the _autodeploy backend.
"""
spec_config = ad_config.speculative_config
if spec_config is not None and spec_config.spec_dec_mode.is_mtp_eagle_one_model():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid testing spec_dec_mode here before AutoDeploy has resolved the checkpoint's MTP layer count? num_nextn_predict_layers is init=False; the input validator remaps it to max_draft_len, leaving it None, and this property treats None as one layer. AD also skips ModelLoader, the only pre-executor writer of that field. As a result every allowed AD MTP config—including the registered smoke/accuracy tests—hits this NotImplementedError. Please resolve the actual mode before this predicate or preserve the existing AD MTP path.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch from abbd34f to 973fd7d Compare August 13, 2026 07:36
2ez4bz added 3 commits August 13, 2026 09:30
* Why?

Overlapped one-model MTP could overstate the cached length or expose too
few FlashInfer pages during generation. This could access an invalid page
and cause a CUDA illegal memory access.

Draft-token accounting could also underreserve scheduler capacity or
produce out-of-range positions near the sequence limit.

* What?

Keep Python and C++ draft-token state synchronized and disable drafting
when the target positions could exceed the sequence limit.

Use the prompt KV boundary for the first generation step when dynamic
length correction is unavailable. For FlashInfer, expose all reserved
generation pages while maintaining the logical KV lengths independently.

Signed-off-by: William Zhang <133824995+2ez4bz@users.noreply.github.com>
* Why?
One-model MTP can consume positions beyond the target verification
span near the maximum sequence length. The shared-KV Gemma4 assistant
could therefore index one rotary row past the model limit and poison
the CUDA context.

* What?
Include shared and non-shared drafter lookahead in the zero-draft
boundary check, with coverage for overlap and non-overlap scheduling.

Signed-off-by: William Zhang <133824995+2ez4bz@users.noreply.github.com>
Signed-off-by: William Zhang <133824995+2ez4bz@users.noreply.github.com>
@2ez4bz
2ez4bz force-pushed the dev-flashinfer-mtp-ima branch from 973fd7d to 65a41e8 Compare August 13, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants